/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief Implementation for the payload carrier protocol.
 * \author Antti Maula <antti.maula@tkk.fi>
 */
#include "fastProtocol.h"
#include "fastProtocolExplicitMulticast.h"
#include "fastProtocolBroadcast.h"
#include "fastProtocol_util.h"
#include "owndebug.h"
#include "ownutils.h"
#include "../connectionList.h"
#include "../connectionListEntry.h"
#include <assert.h>

//*************************************************************************
//*************************************************************************
static EProtocolExit dispatchPacket(TFastProtocol *p,
                                    TConnectionList *l, 
                                    TConnectionListEntry *e,
                                    TFastProtocolHeader *fastheader);
static EProtocolExit dispatchNormalPacket(TFastProtocol *p,
                                          TConnectionList *l, 
                                          TConnectionListEntry *e,
                                          TFastProtocolHeader *fastheader);
static EProtocolExit fastProtocolErrorHandler( TFastProtocol *p, 
                                               TConnectionList *l, 
                                               TConnectionListEntry *e );
static EProtocolExit fastProtocolHandler(TFastProtocol *p,
                                         TConnectionList *l, 
                                         TConnectionListEntry *e);
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************

int fastProtocolInit(TFastProtocol *p)
{
  dPrintG(2,"FastProtocol - Initialized");
  dPrintG(3,"Sizeof FastProtocolHeader is %d bytes", sizeof(TFastProtocolHeader));
  
  assert(sizeof(TFastProtocolHeader) == SIZEOF_TFASTPROTOCOLHEADER);

  return 0;
}
//*************************************************************************

const TProtocolEntry *fastProtocolGetProtocolEntry(TFastProtocol *fp)
{
  TProtocolEntry *ne;

  assert(fp != NULL);

  ne = protocolEntryConstructNew(FASTPROTOCOL_NUMBER,
                                 fp,
                                 (protocolRawHandlerPtr)NULL,
                                 (protocolHandlerPtr)fastProtocolHandler,
                                 (protocolErrorHandlerPtr)fastProtocolErrorHandler);
  // Return the new entry!
  return ne;
}
//*************************************************************************

static EProtocolExit fastProtocolErrorHandler( TFastProtocol *p, 
                                               TConnectionList *l, 
                                               TConnectionListEntry *e )
{
  assert(p != NULL);
  assert(l != NULL);
  assert(e != NULL);

  dPrint(3, "fastProtocol Error handler called!");
  
  // Nothing to to currently.

  return protocolExitNoError;
}
//*************************************************************************

static EProtocolExit fastProtocolHandler(TFastProtocol *p,
                                         TConnectionList *l, 
                                         TConnectionListEntry *e)
{
  EProtocolExit pExit = protocolExitError;
  TFastProtocolHeader *fastheader;
  const unsigned char *dataBuffer;
  int dataBytes;

  // Checks
  assert(p != NULL);
  assert(l != NULL);
  assert(e != NULL);

  // Get easier-to-reference pointers.
  dataBuffer = bufferGetPtr(&e->inputBuffer); // Data buffer
  dataBytes = bufferGetLength(&e->inputBuffer); // Whole buffer len
    
  // Check if we got the whole header part (required for minimal processing)
  assert(dataBytes >= sizeof(TFastProtocolHeader));

  // Typecast the data in struct form for easy data access.
  fastheader = (TFastProtocolHeader *)dataBuffer;
  
  // Check that the protocol number&version matches.
  if (fastheader->protocol_id.protocol_number != FASTPROTOCOL_NUMBER) {
    dPrint(1,"INTERNAL ERROR: Invalid magic number received! (%02x != %02x)",
           fastheader->protocol_id.protocol_number, FASTPROTOCOL_NUMBER);
    return protocolExitError;
    
  }

  // Now, check whether we got the WHOLE packet
  assert(dataBytes >= fastheader->protocol_id.total_size);
  assert(e->state == KConnectionState_Connected);

  // Check that the link is authenticated, otherwise don't accept any
  // fastProtocol traffic.
  if (e->flags & connectionEntryFlag_LinkAuthenticated) {
    if (! (e->flags & (connectionEntryFlag_LinkHub2Hub|connectionEntryFlag_LinkAPService))) {
      //      dPrint(ODTEST,"Received fastProtocol packet not originating from a AP/APService, "
      //       "overriding with connections real ID, '0x%08x'", e->connectionID);
      fastheader->source_id = e->connectionID;
    }

    // The packet is complete! So handle it!
    pExit = dispatchPacket(p, l, e, fastheader);
    
  } else {
    dPrint(ODWARN,"SourceID 0x%08x attempted to send FastProtocol packets, but link is not authenticated! Ignoring.",
           fastheader->source_id);  
    
  }
  return pExit;
}
//*************************************************************************

static EProtocolExit dispatchPacket(TFastProtocol *p,
                                    TConnectionList *l, 
                                    TConnectionListEntry *e,
                                    TFastProtocolHeader *fastheader)
{
  int r = protocolExitNoError;
  
  // Dump the header for debug
  if (debugGetPrintLevel() >= 6)
    dumpPacketHeader(6, fastheader);

  // Check whether this packet is in some special mode.
  if (fastheader->flags & FASTPROTOCOL_FLAG_MODE_EXPLICIT_MULTICAST) {
    // Doing EXPLICIT MULTICAST processing.
    r = dispatchExplicitMulticastPacket(p, l, e, fastheader);
    
  } else if (fastheader->flags & FASTPROTOCOL_FLAG_MODE_BROADCAST) { 
    // Doing BROADCAST processing.
    r = dispatchBroadcastPacket(p, l, e, fastheader);
    
  } else { // Doing ordinary packet trasmit.
    r = dispatchNormalPacket(p, l, e, fastheader);
    
  }
      
  // Return status
  return r;
}
//*************************************************************************

static EProtocolExit dispatchNormalPacket(TFastProtocol *p,
                                          TConnectionList *l, 
                                          TConnectionListEntry *e,
                                          TFastProtocolHeader *fastheader)
{
  int r = protocolExitNoError;
  int cansend = 1;

  // Now, check whether this packet has any 'LINK' flags set:
  //cansend = 1; // No limits on sending currently

  
  if (cansend) {
    
    // Send the data through connectionList
    r = connectionListSendDataByID(l, 
                                   &e->inputBuffer, 
                                   0, // Start from zero - send all!
                                   fastheader->protocol_id.total_size,
                                   fastheader->target_id);
    // Process the connectionList Reply (may send reply!)
    r = handleConnectionListReply(p, l, e, fastheader, r);
  }
  
  return r;
}
//*************************************************************************
//**************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
//*************************************************************************
